home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 49 / Amiga Format CD49 (2000-01-17)(Future Publishing)(GB)(Track 1 of 3)[!][issue 2000-02].iso / -serious- / graphics / gnuplot / gnuplot-3.7.1src / gnuplot-3.7.1 / time.c < prev    next >
C/C++ Source or Header  |  1999-11-29  |  19KB  |  837 lines

  1. #ifndef lint"August", "SepteSid = "$Id: time.c,v 1.5.2.1 1999/08/19 14:35:30 lhecking Exp $";
  2. #endif
  3.  
  4. /* GNUPLOT - time.c */
  5.  
  6. /*[
  7.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  8.  *
  9.  * Permission to use, copy, and distribute this software and its
  10.  * documentation for any purpose with or without fee is hereby granted,
  11.  * provided that the above copyright notice appear in all copies and
  12.  * that both that copyright notice and this permission notice appear
  13.  * in supporting documentation.
  14.  *
  15.  * Permission to modify the software is granted, but not the right to
  16.  * distribute the complete modified source code.  Modifications are to
  17.  * be distributed as patches to the released version.  Permission to
  18.  * distribute binaries produced by compiling modified sources is granted,
  19.  * provided you
  20.  *   1. distribute the corresponding source modifications from the
  21.  *    released version in the form of a patch file along with the binaries,
  22.  *   2. add special version identification to distinguish your version
  23.  *    in addition to the base release version number,
  24.  *   3. provide your name and address as the primary contact for the
  25.  *    support of your modified version, and
  26.  *   4. retain our contact information in regard to use of the base
  27.  *    software.
  28.  * Permission to distribute the released version of the source code along
  29.  * with corresponding source modifications in the form of a patch file is
  30.  * granted with same provisions 2 through 4 for binary distributions.
  31.  *
  32.  * This software is provided "as is" without express or implied warranty
  33.  * to the extent permitted by applicable law.
  34. ]*/
  35.  
  36.  
  37. /* some systems may not implement time very well ; in particular,
  38.  * things might break as the year 2000 approaches.
  39.  * This module either adds a routine gstrptime() to read a formatted time,
  40.  * augmenting the standard suite of time routines provided by ansi,
  41.  * or it completely replaces the whole lot with a new set of routines,
  42.  * which count time relative to the year 2000. Default is to use the
  43.  * new routines. define USE_SYSTEM_TIME to use the system routines, at your
  44.  * own risk. One problem in particular is that not all systems allow
  45.  * the time with integer value 0 to be represented symbolically, which
  46.  * prevents use of relative times.
  47.  */
  48.  
  49.  
  50. #include "plot.h"
  51.  
  52. /* build as a standalone test */
  53.  
  54. #ifdef TEST_TIME
  55.  
  56. # ifdef HAVE_SYS_TIMEB_H
  57. #  include <sys/timeb.h>
  58. #else
  59. /* declare struct timeb */
  60. extern int ftime(struct timeb *);
  61. #endif
  62.  
  63. # define int_error(x,y) fprintf(stderr, "Error: " x "\n")
  64. # define int_warn(x,y) fprintf(stderr, "Warn: " x "\n")
  65.  
  66. /* need (only) these from plot.h */
  67. # define ZERO_YEAR    2000
  68. /* 1st jan, 2000 is a Saturday (cal 1 2000 on unix) */
  69. # define JAN_FIRST_WDAY 6
  70.  
  71. /*  zero gnuplot (2000) - zero system (1970) */
  72. # define SEC_OFFS_SYS    946684800.0
  73.  
  74. /* avg, incl. leap year */
  75. # define YEAR_SEC    31557600.0
  76.  
  77. /* YEAR_SEC / 12 */
  78. # define MON_SEC        2629800.0
  79.  
  80. # define WEEK_SEC    604800.0
  81. # define DAY_SEC        86400.0
  82.  
  83. /*forward decls */
  84. extern char *abbrev_month_names[];
  85. extern char *full_month_names[];
  86. extern char *abbrev_day_names[];
  87. extern char *full_day_names[];
  88.  
  89. #else /* TEST_TIME */
  90.  
  91. # include "setshow.h"        /* for month names etc */
  92.  
  93. #endif /* TEST_TIME */
  94.  
  95. static char *read_int __PROTO((char *s, int nr, int *d));
  96. static int gdysize __PROTO((int yr));
  97.  
  98.  
  99. static char *
  100.  read_int(s, nr, d)
  101. char *s;
  102. int nr, *d;
  103. {
  104.     int result = 0;
  105.  
  106.     while (--nr >= 0 && *s >= '0' && *s <= '9')
  107.     result = result * 10 + (*s++ - '0');
  108.  
  109.     *d = result;
  110.     return (s);
  111. }
  112.  
  113.  
  114.  
  115. #ifndef USE_SYSTEM_TIME
  116.  
  117. /* a new set of routines to completely replace the ansi ones
  118.  * Use at your own risk
  119.  */
  120.  
  121.  
  122. static int mndday[12] =
  123. {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  124.  
  125. static int xstrftime __PROTO((char *buf, int bufsz, char *fmt, struct tm * tm));
  126.  
  127. /* days in year */
  128. static int gdysize(yr)
  129. int yr;
  130. {
  131.  
  132.     if (!(yr % 4)) {
  133.     if ((!(yr % 100)) && yr % 400)
  134.         return (365);
  135.     return (366);
  136.     }
  137.     return (365);
  138. }
  139.  
  140.  
  141. /* new strptime() and gmtime() to allow time to be read as 24 hour,
  142.  * and spaces in the format string. time is converted to seconds from
  143.  * year 2000.... */
  144.  
  145. char *
  146.  gstrptime(s, fmt, tm)
  147. char *s;
  148. char *fmt;
  149. struct tm *tm;
  150. {
  151.     int yday, date;
  152.  
  153.     date = yday = 0;
  154.     tm->tm_mday = 1;
  155.     tm->tm_mon = tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
  156.     /* make relative times work (user-defined tic step) */
  157.     tm->tm_year = ZERO_YEAR;
  158.  
  159.     /* we do not yet calculate wday or yday, so make them illegal
  160.      * [but yday will be read by %j]
  161.      */
  162.  
  163.     tm->tm_yday = tm->tm_wday = -1;
  164.  
  165.     while (*fmt) {
  166.     if (*fmt != '%') {
  167.         if (*fmt == ' ') {
  168.         /* space in format means zero or more spaces in input */
  169.         while (*s == ' ')
  170.             ++s;
  171.         ++fmt;
  172.         continue;
  173.         } else if (*fmt == *s) {
  174.         ++s;
  175.         ++fmt;
  176.         continue;
  177.         } else
  178.         break;        /* literal match has failed */
  179.     }
  180.     /* we are processing a percent escape */
  181.  
  182.     switch (*++fmt) {
  183.     case 'b':        /* abbreviated month name */
  184.         {
  185.         int m;
  186.         for (m = 0; m < 12; ++m)
  187.             if (strnicmp(s, abbrev_month_names[m], strlen(abbrev_month_names[m])) == 0) {
  188.             s += strlen(abbrev_month_names[m]);
  189.             goto found_abbrev_mon;
  190.             }
  191.         /* get here => not found */
  192.         int_warn("Bad abbreviated month name", NO_CARET);
  193.         m = 0;
  194.           found_abbrev_mon:
  195.         tm->tm_mon = m;
  196.         break;
  197.         }
  198.  
  199.     case 'B':        /* full month name */
  200.         {
  201.         int m;
  202.         for (m = 0; m < 12; ++m)
  203.             if (strnicmp(s, full_month_names[m], strlen(full_month_names[m])) == 0) {
  204.             s += strlen(full_month_names[m]);
  205.             goto found_full_mon;
  206.             }
  207.         /* get here => not found */
  208.         int_warn("Bad full month name", NO_CARET);
  209.         m = 0;
  210.           found_full_mon:
  211.         tm->tm_mon = m;
  212.         break;
  213.         }
  214.  
  215.     case 'd':        /* read a day of month */
  216.         s = read_int(s, 2, &tm->tm_mday);
  217.         date++;
  218.         break;
  219.  
  220.     case 'm':        /* month number */
  221.         s = read_int(s, 2, &tm->tm_mon);
  222.         date++;
  223.         --tm->tm_mon;
  224.         break;
  225.  
  226.     case 'y':        /* year number */
  227.         s = read_int(s, 2, &tm->tm_year);
  228.         /* In line with the current UNIX98 specification by
  229.          * The Open Group and major Unix vendors,
  230.          * two-digit years 69-99 refer to the 20th century, and
  231.          * values in the range 00-68 refer to the 21st century.
  232.          */
  233.         if (tm->tm_year <= 68)
  234.         tm->tm_year += 100;
  235.         date++;
  236.         tm->tm_year += 1900;
  237.         break;
  238.  
  239.     case 'Y':
  240.         s = read_int(s, 4, &tm->tm_year);
  241.         date++;
  242.         /* tm->tm_year -= 1900; */
  243.         /* HOE tm->tm_year %= 100; */
  244.         break;
  245.  
  246.     case 'j':
  247.         s = read_int(s, 3, &tm->tm_yday);
  248.         tm->tm_yday--;
  249.         date++;
  250.         yday++;
  251.         break;
  252.  
  253.     case 'H':
  254.         s = read_int(s, 2, &tm->tm_hour);
  255.         break;
  256.  
  257.     case 'M':
  258.         s = read_int(s, 2, &tm->tm_min);
  259.         break;
  260.  
  261.     case 'S':
  262.         s = read_int(s, 2, &tm->tm_sec);
  263.         break;
  264.  
  265.     default:
  266.         int_warn("Bad time format in string", NO_CARET);
  267.     }
  268.     fmt++;
  269.     }
  270.  
  271.     FPRINTF((stderr, "read date-time : %d/%d/%d:%d:%d:%d\n", tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec));
  272.  
  273.     /* now check the date/time entered, normalising if necessary
  274.      * read_int cannot read a -ve number, but can read %m=0 then decrement
  275.      * it to -1
  276.      */
  277.  
  278. #define S (tm->tm_sec)
  279. #define M (tm->tm_min)
  280. #define H (tm->tm_hour)
  281.  
  282.     if (S >= 60) {
  283.     M += S / 60;
  284.     S %= 60;
  285.     }
  286.     if (M >= 60) {
  287.     H += M / 60;
  288.     M %= 60;
  289.     }
  290.     if (H >= 24) {
  291.     if (yday)
  292.         tm->tm_yday += H / 24;
  293.     tm->tm_mday += H / 24;
  294.     H %= 24;
  295.     }
  296. #undef S
  297. #undef M
  298. #undef H
  299.  
  300.     FPRINTF((stderr, "normalised time : %d/%d/%d:%d:%d:%d\n", tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec));
  301.  
  302.     if (date) {
  303.     if (yday) {
  304.  
  305.         if (tm->tm_yday < 0)
  306.         int_error("Illegal day of year", NO_CARET);
  307.  
  308.         /* we just set month to jan, day to yday, and let the
  309.          * normalising code do the work.
  310.          */
  311.  
  312.         tm->tm_mon = 0;
  313.         /* yday is 0->365, day is 1->31 */
  314.         tm->tm_mday = tm->tm_yday + 1;
  315.     }
  316.     if (tm->tm_mon < 0) {
  317.         int_error("illegal month", NO_CARET);
  318.         return (NULL);
  319.     }
  320.     if (tm->tm_mday < 1) {
  321.         int_error("illegal day of month", NO_CARET);
  322.         return (NULL);
  323.     }
  324.     if (tm->tm_mon > 11) {
  325.         tm->tm_year += tm->tm_mon / 12;
  326.         tm->tm_mon %= 12;
  327.     } {
  328.         int days_in_month;
  329.         while (tm->tm_mday > (days_in_month = (mndday[tm->tm_mon] + (tm->tm_mon == 1 && (gdysize(tm->tm_year) > 365))))) {
  330.         if (++tm->tm_mon == 12) {
  331.             ++tm->tm_year;
  332.             tm->tm_mon = 0;
  333.         }
  334.         tm->tm_mday -= days_in_month;
  335.         }
  336.     }
  337.     }
  338.     return (s);
  339. }
  340.  
  341. int gstrftime(s, bsz, fmt, l_clock)
  342. char *s;
  343. int bsz;
  344. char *fmt;
  345. double l_clock;
  346. {
  347.     struct tm tm;
  348.  
  349.     ggmtime(&tm, l_clock);
  350. #if 0
  351.     if ((tm.tm_zone = (char *) malloc(strlen(xtm->tm_zone) + 1)))
  352.     strcpy(tm.tm_zone, xtm->tm_zone);
  353.     /* printf("zone: %s - %s\n",tm.tm_zone,xtm->tm_zone); */
  354. #endif
  355.  
  356.     return (xstrftime(s, bsz, fmt, &tm));
  357. }
  358.  
  359.  
  360. /* some shorthands : check that there is space in the output string
  361.  * Odd-looking defn is dangling-else-safe
  362.  */
  363. #define CHECK_SPACE(n) if ( (l+(n)) <= bsz) ; else return 0
  364.  
  365. /* copy a fixed string, checking that there's room */
  366. #define COPY_STRING(z) CHECK_SPACE(strlen(z)) ; strcpy(s, z)
  367.  
  368. /* format a string, using default spec if none given
  369.  * w and z are width and zero-flag
  370.  * dw and dz are the defaults for these
  371.  * In fact, CHECK_SPACE(w) is not a sufficient test, since
  372.  * sprintf("%2d", 365) outputs three characters
  373.  */
  374.  
  375. #define FORMAT_STRING(dz, dw, x)           \
  376.   if (w==0) { w=(dw); if (!z) z=(dz); }    \
  377.   CHECK_SPACE(w);                          \
  378.   sprintf(s, z ? "%0*d" : "%*d", w, (x) )
  379.  
  380. static int xstrftime(str, bsz, fmt, tm)
  381. char *str;            /* output buffer */
  382. int bsz;            /* space available */
  383. char *fmt;
  384. struct tm *tm;
  385. {
  386.     int l = 0;            /* chars written so far */
  387.  
  388.     char *s = str;
  389.  
  390.     memset(s, '\0', bsz);
  391.  
  392.     while (*fmt != '\0') {
  393.     if (*fmt != '%') {
  394.         if (l >= bsz)
  395.         return (0);
  396.         *s++ = *fmt++;
  397.         l++;
  398.     } else {
  399.  
  400.         /* set up format modifiers */
  401.         int w = 0;
  402.         int z = 0;
  403.         if (*++fmt == '0') {
  404.         z = 1;
  405.         ++fmt;
  406.         }
  407.         while (*fmt >= '0' && *fmt <= '9') {
  408.         w = w * 10 + (*fmt - '0');
  409.         ++fmt;
  410.         }
  411.  
  412.         switch (*fmt++) {
  413.         case '%':
  414.         CHECK_SPACE(1);
  415.         *s = '%';
  416.         break;
  417.  
  418.         case 'a':
  419.         COPY_STRING(abbrev_day_names[tm->tm_wday]);
  420.         break;
  421.  
  422.         case 'A':
  423.         COPY_STRING(full_day_names[tm->tm_wday]);
  424.         break;
  425.  
  426.         case 'b':
  427.         case 'h':
  428.         COPY_STRING(abbrev_month_names[tm->tm_mon]);
  429.         break;
  430.  
  431.         case 'B':
  432.         COPY_STRING(full_month_names[tm->tm_mon]);
  433.         break;
  434.  
  435.  
  436. #if 0
  437.         /* %x not currently supported, so neither is c */
  438.         case 'c':
  439.         if (!xstrftime(s, bsz - l, "%x %X", tm))
  440.             return (0);
  441.         break;
  442. #endif
  443.  
  444.         case 'd':
  445.         FORMAT_STRING(1, 2, tm->tm_mday);    /* %02d */
  446.         break;
  447.  
  448.         case 'D':
  449.         if (!xstrftime(s, bsz - l, "%m/%d/%y", tm))
  450.             return (0);
  451.         break;
  452.  
  453.         case 'H':
  454.         FORMAT_STRING(1, 2, tm->tm_hour);    /* %02d */
  455.         break;
  456.  
  457.         case 'I':
  458.         FORMAT_STRING(1, 2, tm->tm_hour % 12);    /* %02d */
  459.         break;
  460.  
  461.         case 'j':
  462.         FORMAT_STRING(1, 3, tm->tm_yday + 1);    /* %03d */
  463.         break;
  464.  
  465.         /* not in linux strftime man page. Not really needed now */
  466.         case 'k':
  467.         FORMAT_STRING(0, 2, tm->tm_hour);    /* %2d */
  468.         break;
  469.  
  470.         case 'l':
  471.         FORMAT_STRING(0, 2, tm->tm_hour % 12);    /* %2d */
  472.         break;
  473.  
  474.         case 'm':
  475.         FORMAT_STRING(1, 2, tm->tm_mon + 1);    /* %02d */
  476.         break;
  477.  
  478.         case 'M':
  479.         FORMAT_STRING(1, 2, tm->tm_min);    /* %02d */
  480.         break;
  481.  
  482.         case 'p':
  483.         CHECK_SPACE(2);
  484.         strcpy(s, (tm->tm_hour < 12) ? "am" : "pm");
  485.         break;
  486.  
  487.         case 'r':
  488.         if (!xstrftime(s, bsz - l, "%I:%M:%S %p", tm))
  489.             return (0);
  490.         break;
  491.  
  492.         case 'R':
  493.         if (!xstrftime(s, bsz - l, "%H:%M", tm))
  494.             return (0);
  495.         break;
  496.  
  497.         case 'S':
  498.         FORMAT_STRING(1, 2, tm->tm_sec);    /* %02d */
  499.         break;
  500.  
  501.         case 'T':
  502.         if (!xstrftime(s, bsz - l, "%H:%M:%S", tm))
  503.             return (0);
  504.         break;
  505.  
  506.         case 'W':        /* mon 1 day of week */
  507.         {
  508.             int week;
  509.             if (tm->tm_yday <= tm->tm_wday) {
  510.             week = 1;
  511.  
  512.             if ((tm->tm_mday - tm->tm_yday) > 4) {
  513.                 week = 52;
  514.             }
  515.             if (tm->tm_yday == tm->tm_wday && tm->tm_wday == 0)
  516.                 week = 52;
  517.  
  518.             } else {
  519.  
  520.             /* sun prev week */
  521.             int bw = tm->tm_yday - tm->tm_wday;
  522.  
  523.             if (tm->tm_wday > 0)
  524.                 bw += 7;    /* sun end of week */
  525.  
  526.             week = (int) bw / 7;
  527.  
  528.             if ((bw % 7) > 2)    /* jan 1 is before friday */
  529.                 week++;
  530.             }
  531.             FORMAT_STRING(1, 2, week);    /* %02d */
  532.             break;
  533.         }
  534.  
  535.         case 'U':        /* sun 1 day of week */
  536.         {
  537.             int week, bw;
  538.  
  539.             if (tm->tm_yday <= tm->tm_wday) {
  540.             week = 1;
  541.             if ((tm->tm_mday - tm->tm_yday) > 4) {
  542.                 week = 52;
  543.             }
  544.             } else {
  545.             /* sat prev week */
  546.             bw = tm->tm_yday - tm->tm_wday - 1;
  547.             if (tm->tm_wday >= 0)
  548.                 bw += 7;    /* sat end of week */
  549.             week = (int) bw / 7;
  550.             if ((bw % 7) > 1) {    /* jan 1 is before friday */
  551.                 week++;
  552.             }
  553.             }
  554.             FORMAT_STRING(1, 2, week);    /* %02d */
  555.             break;
  556.         }
  557.  
  558.         case 'w':        /* day of week, sun=0 */
  559.         FORMAT_STRING(1, 2, tm->tm_wday);    /* %02d */
  560.         break;
  561.  
  562.         case 'y':
  563.         FORMAT_STRING(1, 2, tm->tm_year % 100);        /* %02d */
  564.         break;
  565.  
  566.         case 'Y':
  567.         FORMAT_STRING(1, 4, tm->tm_year);    /* %04d */
  568.         break;
  569.  
  570. #if 0
  571.         case 'Z':
  572.         COPY_STRING(tm->tm_zone);
  573.         break;
  574. #endif
  575.         }            /* switch */
  576.  
  577.         while (*s != '\0') {
  578.         s++;
  579.         l++;
  580.         }
  581.     }
  582.     }
  583.     return (l);
  584. }
  585.  
  586.  
  587.  
  588. /* time_t  */
  589. double gtimegm(tm)
  590. struct tm *tm;
  591. {
  592.     register int i;
  593.     /* returns sec from year ZERO_YEAR, defined in plot.h */
  594.     double dsec;
  595.  
  596.     dsec = 0;
  597.     if (tm->tm_year < ZERO_YEAR) {
  598.     for (i = tm->tm_year; i < ZERO_YEAR; i++) {
  599.         dsec -= (double) gdysize(i);
  600.     }
  601.     } else {
  602.     for (i = ZERO_YEAR; i < tm->tm_year; i++) {
  603.         dsec += (double) gdysize(i);
  604.     }
  605.     }
  606.     if (tm->tm_mday > 0) {
  607.     for (i = 0; i < tm->tm_mon; i++) {
  608.         dsec += (double) mndday[i] + (i == 1 && (gdysize(tm->tm_year) > 365));
  609.     }
  610.     dsec += (double) tm->tm_mday - 1;
  611.     } else {
  612.     dsec += (double) tm->tm_yday;
  613.     }
  614.     dsec *= (double) 24;
  615.  
  616.     dsec += tm->tm_hour;
  617.     dsec *= 60.0;
  618.     dsec += tm->tm_min;
  619.     dsec *= 60.0;
  620.     dsec += tm->tm_sec;
  621.  
  622.     FPRINTF((stderr, "broken-down time : %d/%d/%d:%d:%d:%d = %g seconds\n", tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, dsec));
  623.  
  624.     return (dsec);
  625. }
  626.  
  627. int ggmtime(tm, l_clock)
  628. struct tm *tm;
  629. /* time_t l_clock; */
  630. double l_clock;
  631. {
  632.     /* l_clock is relative to ZERO_YEAR, jan 1, 00:00:00,defined in plot.h */
  633.     int i, days;
  634.  
  635.     /* dodgy way of doing wday - i hope it works ! */
  636.  
  637.     int wday = JAN_FIRST_WDAY;    /* eg 6 for 2000 */
  638.  
  639.     FPRINTF((stderr, "%g seconds = ", l_clock));
  640.  
  641.     tm->tm_year = ZERO_YEAR;
  642.     tm->tm_mday = tm->tm_yday = tm->tm_mon = tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
  643.     if (l_clock < 0) {
  644.     while (l_clock < 0) {
  645.         int days_in_year = gdysize(--tm->tm_year);
  646.         l_clock += days_in_year * DAY_SEC;    /* 24*3600 */
  647.         /* adding 371 is noop in modulo 7 arithmetic, but keeps wday +ve */
  648.         wday += 371 - days_in_year;
  649.     }
  650.     } else {
  651.     for (;;) {
  652.         int days_in_year = gdysize(tm->tm_year);
  653.         if (l_clock < days_in_year * DAY_SEC)
  654.         break;
  655.         l_clock -= days_in_year * DAY_SEC;
  656.         tm->tm_year++;
  657.         /* only interested in result modulo 7, but %7 is expensive */
  658.         wday += (days_in_year - 364);
  659.     }
  660.     }
  661.     tm->tm_yday = (int) (l_clock / DAY_SEC);
  662.     l_clock -= tm->tm_yday * DAY_SEC;
  663.     tm->tm_hour = (int) l_clock / 3600;
  664.     l_clock -= tm->tm_hour * 3600;
  665.     tm->tm_min = (int) l_clock / 60;
  666.     l_clock -= tm->tm_min * 60;
  667.     tm->tm_sec = (int) l_clock;
  668.  
  669.     days = tm->tm_yday;
  670.  
  671.     /* wday%7 should be day of week of first day of year */
  672.     tm->tm_wday = (wday + days) % 7;
  673.  
  674.     while (days >= (i = mndday[tm->tm_mon] + (tm->tm_mon == 1 && (gdysize(tm->tm_year) > 365)))) {
  675.     days -= i;
  676.     tm->tm_mon++;
  677.     }
  678.     tm->tm_mday = days + 1;
  679.  
  680.     FPRINTF((stderr, "broken-down time : %d/%d/%d:%d:%d:%d\n", tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec));
  681.  
  682.     return (0);
  683. }
  684.  
  685.  
  686.  
  687.  
  688. #else /* USE_SYSTEM_TIME */
  689.  
  690. /* define gnu time routines in terms of system time routines */
  691.  
  692. int gstrftime(buf, bufsz, fmt, l_clock)
  693. char *buf;
  694. int bufsz;
  695. char *fmt;
  696. double l_clock;
  697. {
  698.     time_t t = (time_t) l_clock;
  699.     return strftime(buf, bufsz, fmt, gmtime(&t));
  700. }
  701.  
  702. double gtimegm(tm)
  703. struct tm *tm;
  704. {
  705.     return (double) mktime(tm);
  706. }
  707.  
  708. int ggmtime(tm, l_clock)
  709. struct tm *tm;
  710. double l_clock;
  711. {
  712.     time_t t = (time_t) l_clock;
  713.     struct tm *m = gmtime(&t);
  714.     *tm = *m;            /* can any non-ansi compilers not do this ? */
  715. }
  716.  
  717. #define NOTHING
  718. #define LETTER(L, width, field, extra) \
  719.   case L: s=read_int(s,width,&tm->field); extra; continue
  720.  
  721. /* supplemental routine gstrptime() to read a formatted time */
  722.  
  723. char *
  724.  gstrptime(s, fmt, tm)
  725. char *s;
  726. char *fmt;
  727. struct tm *tm;
  728. {
  729.     FPRINTF((stderr, "gstrptime(\"%s\", \"%s\")\n", s, fmt));
  730.  
  731.     /* linux does not appear to like years before 1902
  732.      * NT complains if its before 1970
  733.      * initialise fields to midnight, 1st Jan, 1970 (for relative times)
  734.      */
  735.     tm->tm_sec = tm->tm_min = tm->tm_hour = 0;
  736.     tm->tm_mday = 1;
  737.     tm->tm_mon = 0;
  738.     tm->tm_year = 70;
  739.     /* oops - it goes wrong without this */
  740.     tm->tm_isdst = 0;
  741.  
  742.     for (; *fmt && *s; ++fmt) {
  743.     if (*fmt != '%') {
  744.         if (*s != *fmt)
  745.         return s;
  746.         ++s;
  747.         continue;
  748.     }
  749.     assert(*fmt == '%');
  750.  
  751.     switch (*++fmt) {
  752.     case 0:
  753.         /* uh oh - % is last character in format */
  754.         return s;
  755.     case '%':
  756.         /* literal % */
  757.         if (*s++ != '%')
  758.         return s - 1;
  759.         continue;
  760.  
  761.         LETTER('d', 2, tm_mday, NOTHING);
  762.         LETTER('m', 2, tm_mon, NOTHING);
  763.         LETTER('y', 2, tm_year, NOTHING);
  764.         LETTER('Y', 4, tm_year, tm->tm_year -= 1900);
  765.         LETTER('H', 2, tm_hour, NOTHING);
  766.         LETTER('M', 2, tm_min, NOTHING);
  767.         LETTER('S', 2, tm_sec, NOTHING);
  768.  
  769.     default:
  770.         int_error("incorrect time format character", NO_CARET);
  771.     }
  772.     }
  773.  
  774.     FPRINTF((stderr, "Before mktime : %d/%d/%d:%d:%d:%d\n", tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec));
  775.     /* mktime range-checks the time */
  776.  
  777.     if (mktime(tm) == -1) {
  778.     FPRINTF((stderr, "mktime() was not happy\n"));
  779.     int_error("Invalid date/time [mktime() did not like it]", NO_CARET);
  780.     }
  781.     FPRINTF((stderr, "After mktime : %d/%d/%d:%d:%d:%d\n", tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec));
  782.  
  783.     return s;
  784. }
  785.  
  786.  
  787. #endif /* USE_SYSTEM_TIME */
  788.  
  789.  
  790. #ifdef TEST_TIME
  791.  
  792. char *abbrev_month_names[] =
  793. {"jan", "feb", "mar", "apr", "may", "jun", "jul",
  794.  "aug", "sep", "oct", "nov", "dec"};
  795. char *full_month_names[] =
  796. {"January", "February", "March", "April", "May",
  797. "June", "July", "August", "September", "October", "November", "December"};
  798.  
  799. char *abbrev_day_names[] =
  800. {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
  801. char *full_day_names[] =
  802. {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
  803.  
  804.  
  805.  
  806. /* either print current time using supplied format, or read
  807.  * supplied time using supplied format
  808.  */
  809.  
  810.  
  811. int main(argc,argv)
  812. int argc;
  813. char *argv[];
  814. {
  815.     char output[80];
  816.  
  817.     if (argc < 2) {
  818.     fputs("usage : test 'format' ['time']\n", stderr);
  819.     exit(EXIT_FAILURE);
  820.     }
  821.     if (argc == 2) {
  822.     struct timeb now;
  823.     struct tm *tm;
  824.     ftime(&now);
  825.     tm = gmtime(&now.time);
  826.     xstrftime(output, 80, argv[1], tm);
  827.     puts(output);
  828.     } else {
  829.     struct tm tm;
  830.     gstrptime(argv[2], argv[1], &tm);
  831.     puts(asctime(&tm));
  832.     }
  833.     exit(EXIT_SUCCESS);
  834. }
  835.  
  836. #endif /* TEST_TIME */
  837.